package com.abxh.ffmpage

import android.media.MediaCodec
import android.media.MediaFormat
import android.util.Log
import android.util.Size
import androidx.camera.core.ImageProxy
import androidx.lifecycle.viewModelScope
import com.abxh.jetpack.IViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.nio.ByteBuffer


/**
 * Created by dab on 2021/9/8 11:23
 */
open class AvViewModel : IViewModel() {
    val flow = MutableSharedFlow<ImageProxy>(5, 10, BufferOverflow.DROP_OLDEST)

    fun start(size: Size) {
        val type = "video/avc"
        val mediaCodec = MediaCodec.createEncoderByType(type)
        val format = MediaFormat.createVideoFormat(type, 480, 640)
        mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        mediaCodec.start();
//        yuvI420=ByteArray((size.width * size.height * 3 / 2))
        viewModelScope.launch {
            flow
//                .map { value ->
//                    getBytes(value)
//                }
//                .map {
//                    coding(mediaCodec, it)
//                }
                .collect {
                    withContext(Dispatchers.IO) {
                        Log.e("TAG", "start: " + it.width)
//                    LogUtils.e("analyze*****: " + it.planes[0].pixelStride + it.planes[1].pixelStride + it.planes[2].pixelStride)
                        it.close()
                    }
                }
        }
    }

    fun put(imageProxy: ImageProxy) {
        viewModelScope.launch {
            flow.emit(imageProxy)
        }
    }

    var i = 0L
    val bufferInfo = MediaCodec.BufferInfo()
    fun coding(mediaCodec: MediaCodec, byteArray: ByteArray) {
        val inputBufIndex: Int = mediaCodec.dequeueInputBuffer(1000)
        val inputBuffer = mediaCodec.getInputBuffer(inputBufIndex) //获取编码器传入数据ByteBuffer
        inputBuffer?.clear();//清除以前数据
        inputBuffer?.put(byteArray);//PCMbuffer需要编码器处理数据
        mediaCodec.queueInputBuffer(
            inputBufIndex, 0, byteArray.size, ((i++) * 1000000 / 30), 0
        );
        //处理完成数据
        var outindex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000) //获取解码数据
        while (outindex >= 0) {
            val outputBuffer = mediaCodec.getOutputBuffer(outindex) //获取编码数据
//            writeData(mMuxer, mOutputBuffers!![index], bufferInfo)
            //outputBuffer 编码器处理完成的数据
            mediaCodec.releaseOutputBuffer(outindex, false) //告诉编码器数据处理完成
        }


    }

    private fun writeData(mMuxer: Any, any: Any, mBufferInfo: Any) {


    }

    //    protected open fun getPTSUs(): Long {
//        var result = System.nanoTime() / 1000L
//        // presentationTimeUs should be monotonic
//        // otherwise muxer fail to write
//        if (result < prevOutputPTSUs) result = prevOutputPTSUs - result + result
//        return result
//    }
    var yuvI420: ByteBuffer? = null
    fun getBytes(imageProxy: ImageProxy): ByteArray {
        //获取图像格式
        val format = imageProxy.format
//            if (format == ImageFormat.YUV_420_888) {
        val planes = imageProxy.planes
        val size = imageProxy.width * imageProxy.height * 3 / 2
        if (null == yuvI420 || yuvI420?.capacity() ?: 0 < size) {
            yuvI420 = ByteBuffer.allocate(size);
        }
        yuvI420?.position(0);
        //获取Y数据，getPixelStride 在Y分量下，总是1，表示Y数据时连续存放的。

        val yBuffer = planes[0].buffer
        //行步长，表示一行的最大宽度，可能等于imageProxy.getWidth(),
        // 也可能大于imageProxy.getWidth()，比如有补位的情况。
        val rowStride = planes[0].rowStride
        //这个字节数组表示，在读取Y分量数据时，跳过补位的部分，因为这些补位的部分没有实际数据，只是为了字节对齐，
        // 如果没有补位 rowStride 等于 imageProxy.getWidth()，这就是一个空数组，否则，数组的长度就刚好是要跳过的长度。
        val skipRow = ByteArray(rowStride - imageProxy.width)
        //这个数组，表示了这一行真实有效的数据。
        val row = ByteArray(imageProxy.width)
        //循环读取每一行
        for (i in 0 until imageProxy.height) {
            yBuffer[row]
            //每一行有效的数据拼接起来就是Y数据。
            yuvI420?.put(row)
            //因为最后一行，没有无效的补位数据，不需要跳过，不是最后一行，才需要跳过无效的占位数据。
            if (i < imageProxy.height - 1) {
                yBuffer[skipRow]
            }
        }

        // 获取U V数据
        // Y分量数据时连续存储的，U V分量可能出现交叉存储，
        // 如果pixelStride值为1，表示UV是分别存储的，planes[1] ={UUUU},planes[2]={VVVV},
        // 这个情况还是比较容易获取的，
        // 如果pixelStride 为2，表示UV是交叉存储的,planes[1] ={UVUV},planes[2]={VUVU}，
        // 这个情况，要获取UV，就要拿一个，丢一个，交替取出，同时，也需要考虑跳过无效的补位数据。
        // 如果pixelStride值为1，表示UV是分别存储的，planes[1] ={UUUU},planes[2]={VVVV},
        // 这个情况还是比较容易获取的，

        // 如果pixelStride 为2，表示UV是交叉存储的,planes[1] ={UVUV},planes[2]={VUVU}，
        // 这个情况，要获取UV，就要拿一个，丢一个，交替取出，同时，也需要考虑跳过无效的补位数据。
        for (i in 0..2) {
            val planeProxy = planes[i]
            val uvPixelStride = planeProxy.pixelStride
            //如果U V是交错存放，行步长就等于imageProxy.getWidth()，同时要考虑有占位数据，会大于imageProxy.getWidth()
            // 如果U V是分离存放，行步长就等于imageProxy.getWidth() /2，同时要考虑有占位数据，会大于imageProxy.getWidth()/2
            val uvRowStride = planeProxy.rowStride
            val uvBuffer = planeProxy.buffer

            //一行一行的处理，uvWidth表示了有效数据的长度。
            val uvWidth = imageProxy.width / 2
            val uvHeight = imageProxy.height / 2
            for (j in 0 until uvHeight) {
                //每次处理一行中的一个字节。
                for (k in 0 until uvRowStride) {
                    //跳过最后一行没有占位的数据，
                    if (j == uvHeight - 1) {
                        //UV没有混合在一起，
                        if (uvPixelStride == 1) {
                            //大于有效数据后，跳出内层循环（k < uvRowStride），不用关心最后的占位数据了。
                            // 因为最后一行没有占位数据。
                            if (k >= uvWidth) {
                                break
                            }
                        } else if (uvPixelStride == 2) {
                            //UV没有混合在一起，大于有效数据后，跳出内层循环（k < uvRowStride），
                            // 不用关心最后的占位数据了。
                            // 因为最后一行没有占位数据。注意这里的有效数据的宽度是imageProxy.getWidth()。
                            //这里为什么要减1呢？因为在UV混合模式下，常规情况是UVUV，但是可能存在UVU的情况，
                            // 就是最后的V是没有的，如果不在这里 减1，在接下来get时，会报越界异常。
                            if (k >= imageProxy.width - 1) {
                                break
                            }
                        }
                    }
                    //对每一个字节，分别取出U数据，V数据。
                    val bt = uvBuffer.get()
                    //uvPixelStride == 1表示U V没有混合在一起
                    if (uvPixelStride == 1) {
                        //k < uvWidth表示是在有效范围内的字节。
                        if (k < uvWidth) {
                            yuvI420!!.put(bt)
                        }
                    } else if (uvPixelStride == 2) {
                        //uvPixelStride == 2 表示U V混合在一起。只取偶数位小标的数据，才是U / V数据，
                        // 奇数位，占位符数据都丢弃，同时这里的有效数据长度是imageProxy.getWidth()，
                        // 而不是imageProxy.getWidth() /2，
                        if (k < imageProxy.width && k % 2 == 0) {
                            yuvI420!!.put(bt)
                        }
                    }
                }
            }
        }
        imageProxy.close()
        //全部读取到YUV数据，以I420格式存储的字节数组。

        //全部读取到YUV数据，以I420格式存储的字节数组。
        return yuvI420!!.array()
        //Camera 角度的旋转处理。分别以顺时针旋转Y， U ，V。
        //Camera 角度的旋转处理。分别以顺时针旋转Y， U ，V。
//                if (rotationDegrees === 90 || rotationDegrees === 270) {
//                    result = rotation(result, imageProxy.width, imageProxy.height, rotationDegrees)
//                }

//            } else {
//                //异常处理，如果有厂商修改了CameraX返回的数据格式。
//            }


    }
}